<template>
  <div class="m-countdown">
    <div class="u-title" :style="titleStyle" v-if="title||$slots.title">
      <slot name="title">{{ title }}</slot>
    </div>
    <div class="m-time">
        <span class="u-prefix" v-if="prefix||$slots.prefix">
          <slot name="prefix">{{ prefix }}</slot>
        </span>
      <span class="u-time-value" :style="valueStyle" v-if="futureTime <= 0">
        <slot name="finish">{{ finishedText }}</slot>
      </span>
      <span class="u-time-value" :style="valueStyle" v-else>
        <slot name="formatter">{{ disposeValue }}</slot>
      </span>
      <span class="u-suffix" v-if="suffix||$slots.suffix">
          <slot name="suffix">{{ suffix }}</slot>
        </span>
    </div>
  </div>
</template>

<script>
export default {
  name: "BigdataCountdown",
  props: {
    // 标题
    title: {
      type: String,
      default: ''
    },
    // 倒计时数值，时间戳时为剩余毫秒数（ms）
    value: {
      type: Number,
      default: undefined
    },
    future: {
      type: Boolean,
      default: true
    },
    // Y：年，M：月，D：日，H：时，m：分钟，s：秒，SSS：毫秒
    format: {
      type: String,
      default: 'HH:mm:ss'
    },
    prefix: {
      type: String,
      default: ''
    },
    suffix: {
      type: String,
      default: ''
    },
    titleStyle: {
      type: Object,
      default: () => {
      }
    },
    valueStyle: {
      type: String,
      default: () => {
      }
    },
    finishedText: {
      type: String,
      default: 'Finished'
    },
  },
  data() {
    return {
      // 未来截止时间戳
      futureTime: 0,
      // 剩余时间戳
      disposeValue: undefined,
      timeTask: null,
      REFRESH_INTERVAL: 1000 / 30,
      restMeta: {
        showMillisecond: null,
        showYear: null,
        showMonth: null,
        showDay: null,
        showHour: null,
        showMinute: null,
        showSecond: null,
      }
    }
  },
  computed: {
    showType() {
      return {
        showMillisecond: this.format.includes('S'),
        showYear: this.format.includes('Y'),
        showMonth: this.format.includes('M'),
        showDay: this.format.includes('D'),
        showHour: this.format.includes('H'),
        showMinute: this.format.includes('m'),
        showSecond: this.format.includes('s'),
      }
    },
  },
  created() {
    this.branch();
  },
  watch: {
    value: function () {
      this.branch();
    }
  },
  methods: {
    branch() {
      let {countDown} = this;
      this.futureTime = this.diffDate(this.value, Date.now())
      countDown()
    },
    leftTime(time, length) {
      if (String(time).length >= length) {
        return time
      }
      return '0'.repeat(length - String(time).length) + time
    },
    timeFormat(time) {
      if (time === null) {
        return '--'
      }

      let subtime = time
      let showTime = this.format
      let millisecond = 0
      if (this.showType.showMillisecond) {
        millisecond = subtime %  1000
        const length = this.format.includes('SSS') ? 3 : (this.format.includes('SS') ? 2 : 1)
        const val = this.leftTime(millisecond, length)
        showTime = showTime.replace('SSS', val).replace('SS', val).replace('S', val)
        this.restMeta.showMillisecond = val
      } else {
        this.restMeta.showMillisecond = null
      }
      subtime = Math.floor(subtime / 1000);
      let year = 0
      let month = 0
      let day = 0
      let hour = 0
      let minute = 0
      let seconds = 0
      if (this.showType.showYear) {
        year = Math.floor(subtime / (60 * 60 * 24 * 30 * 12));
        const length = this.format.includes('YY') ? 2 : 1
        const val = this.leftTime(year, length)
        showTime = showTime.replace('YY', val).replace('Y', val)
        this.restMeta.showYear = val
      } else {
        this.restMeta.showYear = null
      }
      if (this.showType.showMonth) {
        subtime -= year * 60 * 60 * 24 * 30 * 12
        month = Math.floor(subtime / (60 * 60 * 24 * 30));
        const length = this.format.includes('MM') ? 2 : 1
        const val = this.leftTime(month, length)
        showTime = showTime.replace('MM', val).replace('M', val)
        this.restMeta.showMonth = val
      } else {
        this.restMeta.showMonth = null
      }
      if (this.showType.showDay) {
        subtime -= month * 60 * 60 * 24 * 30
        day = Math.floor(subtime / (60 * 60 * 24));
        const length = this.format.includes('DD') ? 2 : 1
        const val = this.leftTime(day, length)
        showTime = showTime.replace('DD', val).replace('D', val)
        this.restMeta.showDay = val
      } else {
        this.restMeta.showDay = null
      }
      if (this.showType.showHour) {
        subtime -= day * 60 * 60 * 24
        hour = Math.floor(subtime / (60 * 60));
        const length = this.format.includes('HH') ? 2 : 1
        const val = this.leftTime(hour, length)
        showTime = showTime.replace('HH', val).replace('H', val)
        this.restMeta.showHour = val
      } else {
        this.restMeta.showHour = null
      }
      if (this.showType.showMinute) {
        subtime -= hour * 60 * 60
        minute = Math.floor(subtime / 60);
        const length = this.format.includes('mm') ? 2 : 1
        const val = this.leftTime(minute, length)
        showTime = showTime.replace('mm', val).replace('m', val)
        this.restMeta.showMinute = val
      } else {
        this.restMeta.showMinute = null
      }
      if (this.showType.showSecond) {
        subtime -= minute * 60
        seconds = Math.floor(subtime)
        const length = this.format.includes('ss') ? 2 : 1
        const val = this.leftTime(seconds, length)
        showTime = showTime.replace('ss', val).replace('s', val)
        this.restMeta.showSecond = val
      } else {
        this.restMeta.showSecond = null
      }
      return showTime
    },
    diffDate(minuend, subtrahend) {
      return Math.max(minuend - subtrahend, 0);
    },
    suspend(isStop) {
      if (isStop) {
        if (this.timeTask) {
          clearInterval(this.timeTask);
          this.timeTask = null;
        }
      } else {
        this.branch();
      }
      return this.disposeValue;
    },
    stopTime(time) {
      let result = true; // stop
      if (time) {
        this.$emit('change', {
          time: time,
          meta: this.restMeta
        });
        result = false;
      } else {
        result = true;
        this.suspend(true);
        this.$emit('finish', true);
      }
      return result;
    },
    countDown() {
      let {REFRESH_INTERVAL, timeTask, diffDate, stopTime, suspend, timeFormat} = this;
      if (timeTask) return;
      let than = this;
      this.timeTask = setInterval(() => {
        let diffTiem = diffDate(than.value, Date.now());
        than.disposeValue = timeFormat(diffTiem);
        stopTime(diffTiem);
      }, REFRESH_INTERVAL);
      this.$once('hook:beforeDestroy', () => {
        suspend(true);
      });
    }
  }
}
</script>

<style scoped>

</style>
